package csc.main;

import java.util.List;
import java.util.Locale;
import csc.io.FileWriter;
import csc.util.Utils;
import java.util.ArrayList;

/**
 * Class used for generating graphical reports.
 *
 * @author Stanislau Dubrouski
 */
class Visualizer {
    private int imageWidth;
    private int imageHeight;

    private List<Colors> colors;

    private List<List<Shift>> realShifts;
    private List<List<Shift>> approximatedShifts;

    public Visualizer() {
    }

    public Visualizer(int imageWidth, int imageHeight, List<List<Shift>> realShifts, List<List<Shift>> approximatedShifts, List<Colors> colors) {
        this.imageWidth = imageWidth;
        this.imageHeight = imageHeight;
        this.realShifts = realShifts;
        this.approximatedShifts = approximatedShifts;
        this.colors = colors;
    }

    public List<List<Shift>> getRealShifts() {
        return realShifts;
    }

    public void setRealShifts(List<List<Shift>> realShifts) {
        this.realShifts = realShifts;
    }

    public List<List<Shift>> getApproximatedShifts() {
        return approximatedShifts;
    }

    public void setApproximatedShifts(List<List<Shift>> approximatedShifts) {
        this.approximatedShifts = approximatedShifts;
    }

    public int getImageWidth() {
        return imageWidth;
    }

    public void setImageWidth(int imageWidth) {
        this.imageWidth = imageWidth;
    }

    public int getImageHeight() {
        return imageHeight;
    }

    public void setImageHeight(int imageHeight) {
        this.imageHeight = imageHeight;
    }

    public List<Colors> getColors() {
        return colors;
    }

    public void setColors(List<Colors> colors) {
        this.colors = colors;
    }


    public boolean generateReport(String fileName, String title) {
        StringBuilder report = new StringBuilder();
        report.append(getHeaders(title));

        report.append("<h3>").append("Average shifts values for color channels follow:").append("</h3>");

        report.append("<table style=\"border-collapse: separate; border-spacing: 2px;\" class=\"averageShifts\" border=\"1\">");
        report.append("<tr>");
        report.append("<td>").append("</td>");
        report.append("<td><strong>").append("Mean shift").append("</strong></td>");
        report.append("<td><strong>").append("Standard deviation").append("</strong></td>");
        report.append("</tr>").append(System.getProperty("line.separator"));

        for (List<Shift> s : realShifts) {
            report.append("<tr>");
            report.append("<td><strong>").append(getColorPairText(realShifts.indexOf(s))).append("</strong></td>");
            report.append("<td>").append(Shift.calculateMeanShift(s)).append("</td>");
            report.append("<td>").append(Shift.calculateShiftStandardDeviation(s)).append("</td>");
            report.append("</tr>");
        }
        report.append("</table>");

        report.append("<h3>").append("Average shifts value for the image follows:").append("</h3>");

        report.append("<table style=\"border-collapse: separate; border-spacing: 2px;\" class=\"averageShifts\" border=\"1\">");
        report.append("<td><strong>").append("Mean shift").append("</strong></td>");
        report.append("<td><strong>").append("Standard deviation").append("</strong></td>");
        report.append("</tr>");

        List<Float> means = new ArrayList<Float>();
        List<Float> devs = new ArrayList<Float>();

        for (List<Shift> s : realShifts) {
            means.add(Shift.calculateMeanShift(s));
            devs.add(Shift.calculateShiftStandardDeviation(s));
        }

        report.append("<tr>");
        report.append("<td>").append(Utils.computeMeanValue(means)).append("</td>");
        report.append("<td>").append(Utils.computeMeanValue(devs)).append("</td>");
        report.append("</tr>");

        report.append("</table>");

        report.append("<table>");

        for (int i = 0; i < realShifts.size(); i++) {

            report.append("<tr>");
            report.append("<td class=\"midHeader\" colspan=\"2\"><h3>")
                    .append("Measured shifts for colors " + getColorPairText(i) + ", (left - LCA, right - ACA)")
                    .append("</h3></td>");
            report.append("</tr>");

            report.append("<tr>");
            report.append("<td>");
            report.append(drawLCAVectors(realShifts.get(i), i + "lca", 100));
            report.append("</td>");
            report.append("<td>");
            report.append(drawACAVectors(realShifts.get(i), i + "approx_aca", 40));
            report.append("</td>");
            report.append("</tr>");
        }

        if (approximatedShifts != null) {
            for (int i = 0; i < approximatedShifts.size(); i++) {
                report.append("<tr>");
                report.append("<td class=\"midHeader\" colspan=\"2\"><h3>")
                        .append("Approximated shifts for colors " + getColorPairText(i) + ", (left - LCA, right - ACA)")
                        .append("</h3></td>");
                report.append("</tr>");
                report.append("<tr>");
                report.append("<td>");
                report.append(drawLCAVectors(approximatedShifts.get(i), i + "lca", 100));
                report.append("</td>");
                report.append("<td>");
                report.append(drawACAVectors(approximatedShifts.get(i), i + "approx_aca", 40));
                report.append("</td>");
                report.append("</tr>");
            }
        }
        report.append("</table>");

        report.append(getFooter());

        boolean writingResult = FileWriter.writeToFile(fileName, report.toString());
        return writingResult;
    }

    private String drawLCAVectors(List<Shift> vectors, String id,
            int multiplier) {

        StringBuilder builder = new StringBuilder();

        builder.append("<svg x=\"" + 0 + "\" y=\"" + 0 + "\" width=\"" + imageWidth + "\" height=\"" + imageHeight + "\">");
        builder.append(getArrowMarker());

        builder.append("<rect x=\"0\" y=\"0\" width=\"" + imageWidth + "\" height=\"" + imageHeight 
                + "\" stroke-width=\"2\" style=\"fill: none; stroke:black; stroke-width: 2;\"/>");

        for (int i = 0; i < vectors.size(); i++) {
            String line = String.format(Locale.US, "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: black;\"  marker-end=\"url(#myMarker)\"/>",
                    vectors.get(i).getBeginVector().getX(),
                    vectors.get(i).getBeginVector().getY(),
                    (vectors.get(i).getEndVector().getX()
                    + (vectors.get(i).getEndVector().getX() - vectors.get(i).getBeginVector().getX()) * multiplier),
                    (vectors.get(i).getEndVector().getY()
                    + (vectors.get(i).getEndVector().getY() - vectors.get(i).getBeginVector().getY()) * multiplier));

            builder.append(line);
            builder.append(System.getProperty("line.separator"));

        }

        builder.append("</svg>");

        String result = builder.toString();
        return result;
    }

    private String drawACAVectors(List<Shift> vectors, String id,
            int multiplier) {

        StringBuilder builder = new StringBuilder();

        builder.append("<svg x=\"" + 0 + "\" y=\"" + 0 + "\" width=\"" + imageWidth + "\" height=\"" + imageHeight + "\">");
        builder.append(getArrowMarker());

        builder.append("<rect x=\"0\" y=\"0\" width=\"" + imageWidth + "\" height=\"" + imageHeight 
                + "\" stroke-width=\"2\" style=\"fill: none; stroke:black; stroke-width: 2;\"/>");

        for (int i = 0; i < vectors.size(); i++) {
            String line = String.format(Locale.US, 
                    "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: black;\"  marker-end=\"url(#myMarker)\"  />",
                    vectors.get(i).getBeginVector().getX(),
                    vectors.get(i).getBeginVector().getY(),
                    vectors.get(i).getBeginVector().getX(),
                    (vectors.get(i).getBeginVector().getY()
                    + (vectors.get(i).getEndVector().getZ() - vectors.get(i).getBeginVector().getZ()) * multiplier));

            builder.append(line);
            builder.append(System.getProperty("line.separator"));
        }

        builder.append("</svg>");

        return builder.toString();
    }

    private String getHeaders(String title) {
        StringBuilder builder = new StringBuilder();

        builder.append("<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta charset=\"UTF-8\"/><title>" + title + "</title></head><body>");
        builder.append(System.getProperty("line.separator"));
        builder.append("<style>h1, h2, h3, h4, h5 {color:#272727;} th, td { padding: 5px; } .midHeader {color:#272727;}</style>");

        return builder.toString();
    }

    private String getFooter() {
        return "</body></html>";
    }

    private String getArrowMarker() {
        return "<defs><marker id=\"myMarker\" viewBox=\"0 0 10 10\" refX=\"1\" refY=\"5\" markerUnits=\"strokeWidth\" "
                + "orient=\"auto\" markerWidth=\"6\" markerHeight=\"10\"><polyline points=\"0,0 10,5 0,10 1,5\" fill=\"black\" />"
                + "</marker></defs>";

    }

    private String getColorPairText(int index) {
        if (index < 0) {
            throw new IllegalArgumentException("index cannot be negative.");
        }
        if ((colors.size() != 2) && (colors.size() != 3)) {
            throw new IllegalStateException("wrong number of colors, cannot proceed - " + colors.size());

        }

        String result = "";

        if (colors.size() == 2) {
            if (index != 0) {
                throw new IllegalArgumentException("index cannot be non-zero for only one color pair");
            }
            result = "Color pair:  " + colors.get(0) + "-" + colors.get(1);
        }
        if (colors.size() == 3) {
            if (index > 2) {
                throw new IllegalArgumentException("index cannot be greater than 2 for three color pairs");
            }
            result = "Color pair:  " + colors.get(index) + "-" + colors.get((index + 1) % 3);
        }

        return result;
    }

}
